﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Xml.Linq;

namespace VisualRibbonEditor
{
	/// <summary>
	/// This is the main window for the application.
	/// </summary>
	public partial class MainWindow : Window
	{
		//globals (these are accessed by other components to do things like get the current ribbonDiffxml or re-render the ribbon when necessary)
		public static Solution CurrentSolution { get; set; }
		public static MainWindow CurrentMainPage { get; set; }

		Solution _solution = new Solution(); //currently loaded solution (holds the ribbonDiffXml)
		BaseRibbon _baseRibbon = new BaseRibbon(); //currently loaded BaseRibbon (holds all the other ribbon details that arent part of the DiffXml)
		XElement _currentTab = null; //the currently selected ribbon tab.
		RibbonButton _currentButton = null; //the currently selected ribbon control.
		private RibbonGroup _currentGroup = null; //the currently selected ribbon group.

		public MainWindow()
		{
			InitializeComponent();

			//hande errors that occur on this thread
			App.Current.DispatcherUnhandledException += (sender, e) =>
			{
				ShowError(e.Exception);
			};
		}


		public void ShowError(Exception ex)
		{
			ShowError(ex, String.Empty);
		}
		public void ShowError(Exception ex, string message)
		{
			this.IsEnabled = true;
			HideInPorgressMessage();
			ErrorDialog errorDialog = new ErrorDialog(ex, message);
			errorDialog.Margin = new Thickness(20);
			ShowPopupDialog(errorDialog, (x) => { /*nothing*/ });
		}



		/// <summary>
		/// Window loaded
		/// </summary>
		private void Window_Loaded(object sender, RoutedEventArgs e)
		{
			CurrentSolution = _solution;
			CurrentMainPage = this;

			ClearAndDisableUI();
		}

		/// <summary>
		/// This is called when solution has been retrieved from CRM.
		/// </summary>
		private void SolutionLoaded()
		{
			//make sure we have both the baseRibbon and the solution (diff).
			if (_solution.RibbonDiffXml != null &&
				_baseRibbon.RibbonXml != null &&
				Connection.CurrentConnection.OrganizationId != Guid.Empty &&
				!String.IsNullOrEmpty(Connection.CurrentConnection.BaseLanguage)
				)
			{

				SetEntityNameStatus(_solution.EntityName);

				this.IsEnabled = true;
				InProgressBorder.Visibility = Visibility.Collapsed;

				//populate ribbon type picklist then select first one (which will render ribbon)
				PopulateRibbonTypes();
				RibbonType.SelectedIndex = 0;

				//enable toolbar buttons
				SetButtonEnabled(SaveButton, true);
				SetButtonEnabled(NewButton, true);
				SetButtonEnabled(NewGroup, true);
				//SetButtonEnabled(NewMenuButton, true);
				SetButtonEnabled(HideTab, true);
				SetButtonEnabled(UnHideDialogButton, true);
				SetButtonEnabled(ViewWebResourcesButton, true);
				SetButtonEnabled(ViewXmlButton, true);
				RibbonType.IsEnabled = true;

				EditorTabStackBorder.Visibility = Visibility.Visible;
				EditorTabStack.Visibility = Visibility.Visible;
				EditorTabStack.IsEnabled = true;

				XmlDetailStackBorder.Visibility = Visibility.Visible;
			}
		}

		/// <summary>
		/// User clicks the open button
		/// </summary>
		private void OpenButton_Click(object sender, RoutedEventArgs e)
		{
			//show the connection dialog if no connection yet.
			if(!Connection.CurrentConnection.IsLoaded)
			{
				ConnectDialog connectDialog = new ConnectDialog();
				ShowPopupDialog(connectDialog, (ok) =>
				{
					if (ok != null && (bool)ok)
					{
						//if connection info is good then we can show open dialog
						ShowOpenRibbonDialog();
					}
				});
			}
			else
			{
				//if connection is already loaded then just show open dialog.
				ShowOpenRibbonDialog();
			}
		}

		public void ShowConnectionStatus()
		{
			string text = "Disconnected";
			if (Connection.CurrentConnection.IsConnected) text = "Connected to {OrgName} on {ServerHostName} {ServerType}";
			ShowConnectionStatus(text, Connection.CurrentConnection);
		}

		public void ShowConnectionStatus(string text)
		{
			ShowConnectionStatus(text, Connection.CurrentConnection);
		}
		public void ShowConnectionStatus(string text, Connection connection)
		{
			ConnectionInfo cInfo = new ConnectionInfo(connection);
			text = text.Replace("{OrgName}", cInfo.OrganizationName);
			text = text.Replace("{ServerHostName}", cInfo.ServerHostName);
			text = text.Replace("{BaseLcid}", cInfo.BaseLcid);
			if (cInfo.CrmOnline)
			{
				text = text.Replace("{ServerType}", "(CRM Online)");
			}
			else if (cInfo.ADFSMode)
			{
				text = text.Replace("{ServerType}", "(ADFS/IFD)");
			}
			else
			{
				text = text.Replace("{ServerType}", "(On-Premises)");
			}
			this.SetConnectionStatus(text);
			this.SetServerVersionStatus(connection.ServerVersion);
		}
		public void SetConnectionStatus(string text)
		{
			if (text == null) text = String.Empty;

			SbConnectionDetails.Text = text;
			SbConnectionDetails.ToolTip = text;
		}
		public void ClearEntityNameStatus()
		{
			SetEntityNameStatus(String.Empty);
		}
		public void SetEntityNameStatus(string text)
		{
			if (String.IsNullOrEmpty(text)) text = String.Empty;
			else text = "Ribbon: " + text;
			SbEntityName.Text = text;
			SbEntityName.ToolTip = text;
		}

		/// <summary>
		/// shows the open ribbon dialog
		/// </summary>
		public void ShowOpenRibbonDialog()
		{
			OpenEntityDialog openDialog = new OpenEntityDialog();
			this.ShowPopupDialog(openDialog, (result) =>
			{
				//if user selected an entity, then load the ribbons
				if(result != null)
				{
					string entity = result.ToString();

					ClearEntityNameStatus();
					WebResourceManager.ClearCache();
					ClearServerVersionStatus();
					ShowInProgressMessage("Loading " + entity + " Ribbon...");
					SetInProgressSubText("Just a moment");

					ShowConnectionStatus("Connecting to {OrgName} on {ServerHostName} {ServerType}", Connection.CurrentConnection);
					// Test connection. OrganizationId is loaded here
					Connection.CurrentConnection.TestConnection(true, true, (passed, errorDescription) =>
					{
						if (!passed)
						{
							string errorMessage = "Failed to connect to CRM.";
							if (!String.IsNullOrEmpty(errorDescription)) errorMessage += (Environment.NewLine + errorDescription);
							MessageBox.Show(errorMessage);
							return;
						}
						ShowConnectionStatus("Connected to {OrgName} on {ServerHostName} {ServerType}", Connection.CurrentConnection);
						// Load base language
						Connection.CurrentConnection.LoadBaseLanguage((loaded, errorDescription2) =>
						{
							if (!loaded)
							{
								string errorMessage = "Failed to load base organization language.";
								if (!String.IsNullOrEmpty(errorDescription2)) errorMessage += (Environment.NewLine + errorDescription2);
								MessageBox.Show(errorMessage);
								return;
							}
						});
						// OrganizaitonId, server version and base language are loaded
						// Load Meta
						Meta.GetElementTypes();
						switch (Connection.CurrentConnection.ServerVersion.Major)
						{
							case 6:
								Meta.SwitchToVersion6();
								break;
							case 7:
								Meta.SwitchToVersion7();
								break;
							case 8:
								Meta.SwitchToVersion8();
								break;
							default:
								Meta.SwitchToVersion5();
								break;
						}
						// Load Solution
						SolutionLoaded();

						// Remember entityname used
						if (Connection.CurrentConnection.SaveConnectionInfo)
						{
							Connection.CurrentConnection.EntityName = entity;
							Connection c = Connection.CurrentConnection;
							foreach (Connection connection in c.Connections)
							{
								if (c.GetId() == connection.GetId())
								{
									// Modify existing Connection
									connection.EntityName = c.EntityName;
									break;
								}
							}
							// Save connection info
							Connection.CurrentConnection.Save();
						}
					});


					//load a new baseribbon
					_baseRibbon = new BaseRibbon();
					_baseRibbon.LoadAsync(entity, SolutionLoaded);

					//load the solution
					_solution = new Solution();
					CurrentSolution = _solution;
					_solution.Load(entity, SolutionLoaded);
				}
			});
		}
		/*
		private bool MissingWebResourceDirectiveExist()
		{
			XElement xCommandDefinitions = MainWindow.CurrentSolution.RibbonDiffXml.Descendants("CommandDefinitions").FirstOrDefault();
			if (xCommandDefinitions != null)
			{
				foreach (XElement commandDefinition in xCommandDefinitions.Descendants("CommandDefinition"))
				{
					XElement actions = commandDefinition.Descendants("Actions").FirstOrDefault();
					if (actions != null)
					{
						foreach (XElement javaScriptFunction in actions.Descendants("JavaScriptFunction"))
						{
							string library = javaScriptFunction.Attribute("Library").Value;
							if (!library.ToLower().StartsWith("$webresource:") && !library.StartsWith("/")) return true;
						}
					}
				}
			}
			return false;
		}
		*/
		private bool MissingLabelsExist()
		{
			//can't exist if only one language provisioned
			if (MainWindow.CurrentSolution.ProvisionedLanguages.Count <= 1) return false;
			//search for missing labels for all provisioned languages
			XElement xLocLabels = MainWindow.CurrentSolution.RibbonDiffXml.Descendants("LocLabels").FirstOrDefault();
			if (xLocLabels != null)
			{
				foreach (XElement locLabel in xLocLabels.Descendants("LocLabel"))
				{
					foreach (string lang in MainWindow.CurrentSolution.ProvisionedLanguages)
					{
						XElement title = locLabel.Descendants("Titles").Elements().Where(x => x.Attribute("languagecode").Value == lang).FirstOrDefault();
						if (title == null) return true;
					}
				}
			}
			return false;
		}

		/// <summary>
		/// User clicks the save button
		/// </summary>
		private void SaveCurrentRibbon(object sender, RoutedEventArgs e)
		{
			/*
			if (MissingWebResourceDirectiveExist())
			{
				MessageBoxResult mdResult = MessageBox.Show("One or more JavaScriptionFunction actions are missing $webresource directive. Would you like to fix this?\r\n\r\nNote: an error occures when you try use libraries without $webresource directive on CRM 2011 with Update Rollup 12 or later installed.\r\nIf you press Yes, Visual Ribbon Editor will add missing $webresource directive to existing library names. You may modify library name any time later.", "Fix missing $webresource directives?", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);
				if (mdResult == MessageBoxResult.Yes)
				{
					// Add missing $webresource directives to all JavaScriptionFunction actions
					XElement xCommandDefinitions = MainWindow.CurrentSolution.RibbonDiffXml.Descendants("CommandDefinitions").FirstOrDefault();
					if (xCommandDefinitions != null)
					{
						foreach (XElement commandDefinition in xCommandDefinitions.Descendants("CommandDefinition"))
						{
							XElement actions = commandDefinition.Descendants("Actions").FirstOrDefault();
							if (actions != null)
							{
								foreach (XElement javaScriptFunction in actions.Descendants("JavaScriptFunction"))
								{
									string library = javaScriptFunction.Attribute("Library").Value;
									if (!library.ToLower().StartsWith("$webresource:") && !library.StartsWith("/"))
									{
										javaScriptFunction.Attribute("Library").SetValue("$webresource:" + library);
									}
								}
							}
						}
					}
				}
			}
			*/
			if (MissingLabelsExist())
			{
				MessageBoxResult mlResult = MessageBox.Show("One or more missing labels for provisioned languages were detected. Would you like to fix this?\r\n\r\nNote: if you press Yes, Visual Ribbon Editor will add missing labels for all provisioned languages. Labels will default to base language values. You can modify these labels any time later.", "Fix missing labels?", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);
				if (mlResult == MessageBoxResult.Yes)
				{
					//add missing labels for all provisioned languages
					XElement xLocLabels = MainWindow.CurrentSolution.RibbonDiffXml.Descendants("LocLabels").FirstOrDefault();
					if (xLocLabels != null)
					{
						foreach (XElement locLabel in xLocLabels.Descendants("LocLabel"))
						{
							// get label text for default language
							string defaultLanguageText = String.Empty;
							XElement xDefaultLanguageText = locLabel.Descendants("Titles").Elements().Where(x => x.Attribute("languagecode").Value == Connection.CurrentConnection.BaseLanguage).FirstOrDefault();
							if (xDefaultLanguageText != null) defaultLanguageText = xDefaultLanguageText.Attribute("description").Value;

							foreach (string lang in MainWindow.CurrentSolution.ProvisionedLanguages)
							{
								XElement title = locLabel.Descendants("Titles").Elements().Where(x => x.Attribute("languagecode").Value == lang).FirstOrDefault();
								if (title == null)
								{
									//add title element
									XElement newTitle = new XElement("Title");
									newTitle.SetAttributeValue("languagecode", lang);
									newTitle.SetAttributeValue("description", defaultLanguageText);
									locLabel.Descendants("Titles").First().Add(newTitle);
								}
							}
						}
					}
				}
			}

			MessageBoxResult result = MessageBox.Show("Are you sure you want to save changes to this ribbon? (it may take a few minutes to save)", "Save Ribbon?", MessageBoxButton.OKCancel);
			if (result == MessageBoxResult.OK)
			{
				//save the solution
				ShowInProgressMessage("Saving Ribbon...");
				_solution.Save(HideInPorgressMessage);
			}
		}

		public void ClearAndDisableUI()
		{
			SetButtonEnabled(SaveButton, false, true);
			SetButtonEnabled(NewButton, false, true);
			SetButtonEnabled(NewGroup, false, true);
			//SetButtonEnabled(NewMenuButton, false, true);
			SetButtonEnabled(DeleteButton, false, true);
			SetButtonEnabled(HideButton, false, true);
			SetButtonEnabled(HideTab, false, true);
			SetButtonEnabled(UnHideDialogButton, false, true);
			SetButtonEnabled(ViewXmlButton, false, true);

			RibbonType.IsEnabled = false;
			RibbonType.Items.Clear();
			TabStack.Children.Clear();
			DeselectCurrentButtonOrGroup();
			RibbonStack.Children.Clear();
			SelectedButtonIdText.Text = String.Empty;
			EditorTabStackBorder.Visibility = Visibility.Hidden;
			EditorTabStack.Visibility = Visibility.Hidden;
			EditorTabStack.IsEnabled = false;

			XmlDetailStackBorder.Visibility = Visibility.Hidden;
			XmlDetailStack.Children.Clear();
		}

		/// <summary>
		/// populates the RibbonType picklist
		/// (different types are available if editing the application ribbon)
		/// </summary>
		private void PopulateRibbonTypes()
		{
			RibbonType.Items.Clear();
			if (_solution.EntityName == "application")
			{
				//load these for the Application ribbon
				RibbonType.Items.Add(new ComboBoxItem() { Content = "Dashboard", Tag = "Mscrm.DashboardTab" });
				RibbonType.Items.Add(new ComboBoxItem() { Content = "Basic Home Tab", Tag = "Mscrm.BasicHomeTab" });
				RibbonType.Items.Add(new ComboBoxItem() { Content = "All Entity Forms", Tag = "Mscrm.Form." });
				RibbonType.Items.Add(new ComboBoxItem() { Content = "All Entity Homepages", Tag = "Mscrm.HomepageGrid." });
				RibbonType.Items.Add(new ComboBoxItem() { Content = "All Entity Sub-Grids", Tag = "Mscrm.SubGrid." });
				RibbonType.Items.Add(new ComboBoxItem() { Content = "Advanced Find", Tag = "Mscrm.AdvancedFind" });
				//RibbonType.Items.Add(new ComboBoxItem() { Content = "Legacy Office Toolbar", Tag = "Mscrm.LegacyOfficeToolbar" });
				//RibbonType.Items.Add(new ComboBoxItem() { Content = "Legacy Office Menubar", Tag = "Mscrm.LegacyOfficeMenubar" });
				//RibbonType.Items.Add(new ComboBoxItem() { Content = "Outlook 14 Global Toolbar", Tag = "Mscrm.Outlook14GlobalToolbar" });
				//RibbonType.Items.Add(new ComboBoxItem() { Content = "Outlook 14 Global Menubar", Tag = "Mscrm.Outlook14GlobalMenubar" });
				//RibbonType.Items.Add(new ComboBoxItem() { Content = "WebResource Editor", Tag = "Mscrm.WebResourceEditTab" });
				//RibbonType.Items.Add(new ComboBoxItem() { Content = "Form Editor", Tag = "Mscrm.FormEditor" });
				//RibbonType.Items.Add(new ComboBoxItem() { Content = "Appointment Bool", Tag = "Mscrm.AptbookTab" });
				RibbonType.Items.Add(new ComboBoxItem() { Content = "Dashboard Editor", Tag = "Mscrm.DashboardEditorTab" });
				RibbonType.Items.Add(new ComboBoxItem() { Content = "Visualization Designer", Tag = "Mscrm.VisualizationDesignerTab" });
			}
			else
			{
				//for CRM entity ribbons, just load these..
				RibbonType.Items.Add(new ComboBoxItem() { Content = "Form", Tag = "Mscrm.Form" });
				RibbonType.Items.Add(new ComboBoxItem() { Content = "Homepage", Tag = "Mscrm.HomepageGrid" });
				RibbonType.Items.Add(new ComboBoxItem() { Content = "Sub-Grid", Tag = "Mscrm.SubGrid" });
				//RibbonType.Items.Add(new ComboBoxItem() { Content = "ISV", Tag = "Mscrm.Isv" });
			}
		}

		/// <summary>
		/// User changes the ribbon type, need to re-render tabs and ribbon
		/// </summary>
		private void RibbonType_SelectionChanged(object sender, SelectionChangedEventArgs e)
		{
			// Deselect Button or Group
			DeselectCurrentButtonOrGroup();
			// Toggle "Hide Button" button enabled/disabled
			SetButtonEnabled(HideButton, false);
			// Render Tabs
			RenderTabs();
		}
		/// <summary>
		/// Renders ribbon tabs for currently selected ribbon type
		/// </summary>
		private void RenderTabs()
		{
			if (RibbonType.SelectedItem != null)
			{
				ComboBoxItem item = (ComboBoxItem)RibbonType.SelectedItem;
				RenderTabs(item.Tag.ToString());
			}
		}
		/// <summary>
		/// Renders ribbon tabs
		/// </summary>
		/// <param name="tabType">will show all tabs that have an ID that starts with this string</param>
		private void RenderTabs(string tabType)
		{
			if( _solution == null || _solution.RibbonDiffXml == null)
			{
				return;
			}

			TabStack.Children.Clear();

			//find all the tabs for this tab-type:
			foreach (XElement tab in _baseRibbon.RibbonXml.Descendants("Tab"))
			{
				string id = tab.Attribute("Id").Value;
				if (id.StartsWith(tabType))
				{
					//don't add tab if hidden
					if (Hider.IsControlHidden(id)) continue;
					//add a tab control to the Tab stackpanel for each tab:
					Tab tabButton = new Tab();
					tabButton.CrmId = id;
					tabButton.DisplayName = " " + id.Substring(id.LastIndexOf('.') + 1) + " ";
					tabButton.Margin = new Thickness(5,0,0,0);
					tabButton.Tag = tab;
					tabButton.Click += new RoutedEventHandler(tabButton_Click);

					//nudge the tab down a bit so it overlaps the bottom border of the tab stackpanel
					tabButton.RenderTransform = new TranslateTransform() { Y = 1 };

					TabStack.Children.Add(tabButton);
				}
			}

			//automatically select the first tab if available
			if (TabStack.Children.Count > 0)
			{
				Tab firstTab = (Tab)TabStack.Children[0];
				firstTab.IsHilighted = true;
				tabButton_Click(firstTab, new RoutedEventArgs());
			}
			// Toggle Hide Tab button enabled/disabled
			SetButtonEnabled(HideTab, (_currentTab != null));
		}

		/// <summary>
		/// When clicking on a tab: set the _currentTab var and then render the ribbon
		/// </summary>
		void tabButton_Click(object sender, RoutedEventArgs e)
		{
			Tab tabButton = (Tab)sender;
			XElement tab = (XElement)tabButton.Tag;
			_currentTab = tab;
			RenderRibbon();
		}


		/// <summary>
		/// Element sorting function (sorts based on sequence attribute)
		/// </summary>
		private string SortElements(XElement el)
		{
			XAttribute seq = el.Attribute("Sequence");
			if (seq != null)
			{
				int seqInt = int.Parse(seq.Value);
				return string.Format("{0:00000.00}", seqInt);
			}
			return string.Empty;
		}

		public XElement ShowSectionLinesGroup = null;

		public void TurnOnSectionLines()
		{
			if (_currentGroup != null)
			{
				ShowSectionLinesGroup = _currentGroup.SourceElement;
				RenderRibbon();
			}
			if (_currentButton != null)
			{
				XElement parentGroup = _currentButton.SourceElement.Ancestors("Group").FirstOrDefault();
				if (parentGroup != null)
				{
					ShowSectionLinesGroup = parentGroup;
					RenderRibbon();
				}
			}
		}

		public void TurnOffSectionLines()
		{
			if (ShowSectionLinesGroup != null)
			{
				ShowSectionLinesGroup = null;
				RenderRibbon();
			}
		}

		public void ToggleSectionLines(TextBlock textLink)
		{
			if (ShowSectionLinesGroup != null)
			{
				TurnOffSectionLines();
				if (textLink != null) textLink.Text = "Show Sections";
			}
			else
			{
				TurnOnSectionLines();
				if (textLink != null) textLink.Text = "Hide Sections";
			}
		}

		/// <summary>
		/// Renders the current tab
		/// </summary>
		public void RenderRibbon()
		{
			RibbonStack.Children.Clear();

			if (_currentTab == null) return;
			XElement tab = _currentTab;

			foreach (XElement group in tab.Descendants("Group").OrderBy(SortElements))
			{
				// Check if Modern Client group
				XElement maxSize = tab.Element("Scaling").Elements("MaxSize").Where(x => x.AttributeValue("GroupId") == group.AttributeValue("Id")).FirstOrDefault();
				if (maxSize == null) continue; // v2013: Modern Client buttons do not have correspoding MaxSize element in Scaling. Skipping

				//create the ribbon group
				RibbonGroup ribbonGroup = new RibbonGroup(group);

				//re-select current group
				if (_currentGroup != null && _currentGroup.SourceElement == group)
				{
					_currentGroup = ribbonGroup;
					_currentGroup.IsSelected = true;
					if (ShowSectionLinesGroup == group)
					{
						_currentGroup.ShowTemplateAliasPreview = true;
					}
				}

				//wire up the click handler for the group
				ribbonGroup.Click += new RoutedEventHandler(RibbonGroup_Click);

				//add the group to the ribbon stackpanel
				RibbonStack.Children.Add(ribbonGroup);

				foreach (XElement el in Helpers.GetGroupControlsOrdered(group))
				{
					//create the button
					RibbonButton button = new RibbonButton(el);

					//if this is a re-rendering of the "current" button, then it should be selected.
					//and we need to re-set the currentbutton pointer to this button control.
					if (_currentButton != null && _currentButton.SourceElement == el)
					{
						_currentButton = button;
						button.IsSelected = true;
						if (ShowSectionLinesGroup == group)
						{
							ribbonGroup.ShowTemplateAliasPreview = true;
						}
					}

					//wire up the click event handler
					button.MouseLeftButtonDown += button_MouseLeftButtonDown;

					//add the button to the current ribbon group.
					ribbonGroup.AddRibbonButton(button);
				}
			}
		}


		private void DeselectCurrentButtonOrGroup()
		{
			if (_currentButton != null)
			{
				_currentButton.IsSelected = false;
				_currentButton = null;
			}
			if (_currentGroup != null)
			{
				_currentGroup.IsSelected = false;
				_currentGroup = null;
			}
		}

		void RibbonGroup_Click(object sender, RoutedEventArgs e)
		{
			DeselectCurrentButtonOrGroup();

			_currentGroup = (RibbonGroup)sender;
			_currentGroup.IsSelected = true;
			SelectedButtonIdText.Text = _currentGroup.SourceElement.Name.LocalName + ": " + _currentGroup.SourceElement.AttributeValue("Id");

			GroupDetails details = new GroupDetails(_currentGroup.SourceElement);
			XmlDetailStack.Children.Clear();
			XmlDetailStack.Children.Add(details);

			DetailTab.IsHilighted = true;
			ActionTab.Visibility = System.Windows.Visibility.Collapsed;
			DisplayRulesTab.Visibility = System.Windows.Visibility.Collapsed;
			EnableRulesTab.Visibility = System.Windows.Visibility.Collapsed;

			SetButtonEnabled(HideButton, false);
			SetButtonEnabled(DeleteButton, Deleter.GroupCanBeDeleted(_currentGroup.SourceElement));
			SetButtonEnabled(NewButton, true);
			//SetButtonEnabled(NewMenuButton, true);


			//If the user clicks on a different group then automatically hide the section lines.
			if (ShowSectionLinesGroup != null && _currentGroup.SourceElement != ShowSectionLinesGroup)
			{
				ShowSectionLinesGroup = null;
				RenderRibbon();
			}


		}

		/// <summary>
		/// User clicks on a ribbon control
		/// </summary>
		void button_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			DeselectCurrentButtonOrGroup();
			ActionTab.Visibility = System.Windows.Visibility.Visible;
			DisplayRulesTab.Visibility = System.Windows.Visibility.Visible;
			EnableRulesTab.Visibility = System.Windows.Visibility.Visible;

			RibbonButton b = (RibbonButton)sender;
			_currentButton = b;
			_currentButton.IsSelected = true;


			string controlId = _currentButton.SourceElement.AttributeValue("Id");
			SelectedButtonIdText.Text = _currentButton.SourceElement.Name.LocalName + ": " + controlId;

			DetailTab.IsHilighted = true;
			ActionTab.IsHilighted = false;
			DisplayRulesTab.IsHilighted = false;
			EnableRulesTab.IsHilighted = false;
			DetailTab_Click(DetailTab, new RoutedEventArgs());

			//custom buttons can be deleted. system buttons can be hidden.
			bool isCustomButton = Helpers.IsButtonEditable(_currentButton.SourceElement.AttributeValue("Id"));
			SetButtonEnabled(HideButton, !isCustomButton);
			SetButtonEnabled(DeleteButton, isCustomButton);
			SetButtonEnabled(NewButton, true);
			//SetButtonEnabled(NewMenuButton, true);

			//turn off showing section lines if we've selected a button in a different group
			if (ShowSectionLinesGroup != null)
			{
				XElement parentGroup = _currentButton.SourceElement.Ancestors("Group").FirstOrDefault();
				if (parentGroup != null && parentGroup != ShowSectionLinesGroup)
				{
					ShowSectionLinesGroup = null;
					RenderRibbon();
				}
			}
		}

		private void DetailTab_Click(object sender, RoutedEventArgs e)
		{
			if (_currentButton != null)
			{
				XmlDetailStack.Children.Clear();
				ButtonDetails details = new ButtonDetails(_currentButton.SourceElement);
				this.XmlDetailStack.Children.Add(details);
			}
		}

		private void ActionTab_Click(object sender, RoutedEventArgs e)
		{
			if (_currentButton != null)
			{
				LoadButtonInfo(_currentButton.SourceElement, "Actions");
			}
		}

		private void DisplayRulesTab_Click(object sender, RoutedEventArgs e)
		{
			if (_currentButton != null)
			{
				LoadButtonInfo(_currentButton.SourceElement, "DisplayRules");
			}
		}

		private void EnableRulesTab_Click(object sender, RoutedEventArgs e)
		{
			if (_currentButton != null)
			{
				LoadButtonInfo(_currentButton.SourceElement, "EnableRules");
			}
		}

		private void LoadButtonInfo(XElement button, string type)
		{
			XmlDetailStack.Children.Clear();

			if (button.Name != "Button")
			{
				UnsupportedControl unsupported = new UnsupportedControl(button.Name + " controls are not supported by this application");
				XmlDetailStack.Children.Add(unsupported);
				return;
			}

			if (button.Attribute("Command") == null)
			{
				UnsupportedControl unsupported = new UnsupportedControl("\"Command\" attribute was not found");
				XmlDetailStack.Children.Add(unsupported);
				return;
			}

			string command = button.Attribute("Command").Value;
			XElement CommandDefinition = _baseRibbon.RibbonXml.Descendants("CommandDefinition").Where(x => x.Attribute("Id").Value == command).First();

			XElement subElement = CommandDefinition.Element(type);

			bool editable = Helpers.IsButtonEditable(button.Attribute("Id").Value);

			ElementEditor rootElement = new ElementEditor(subElement, editable);
			rootElement.CanBeDeleted = false;
			XmlDetailStack.Children.Add(rootElement);
			rootElement.Expanded = true;
		}


		private void ViewXmlButton_Click(object sender, RoutedEventArgs e)
		{
			if (_solution != null && _solution.RibbonDiffXml != null)
			{
				ViewXmlDialog dialog = new ViewXmlDialog();
				dialog.Margin = new Thickness(20, 20, 20, 20);
				ShowPopupDialog(dialog, (x) => { /* nothing */ });
			}
		}

		/// <summary>
		/// User clicks "New Button"
		/// </summary>
		private void NewButton_Click(object sender, RoutedEventArgs e)
		{
			if (_currentButton != null || _currentGroup != null)
			{
				//show the new button dialog, and if user clicked ok then re-render the ribbon.
				NewButtonDialog dlg;
				if (_currentButton != null)
				{
					dlg = new NewButtonDialog(_currentButton.SourceElement.Parent);
				}
				else
				{
					dlg = new NewButtonDialog(_currentGroup.SourceElement.Element("Controls"));
				}
				ShowPopupDialog(dlg, (result) =>
				{
					if (result != null)
					{
						RenderRibbon();
					}
				});
			}
			else
			{
				MessageBox.Show("Select a button or group before inserting a new button.");
			}
		}

		/// <summary>
		/// User clicked the delete button
		/// </summary>
		private void DeleteButton_Click(object sender, RoutedEventArgs e)
		{
			if (_currentButton != null)
			{
				//Delete button:
				MessageBoxResult result =
					MessageBox.Show("Are you sure you want to delete the selected button?",
						"Delete Ribbon Button", MessageBoxButton.OKCancel);

				if (result == MessageBoxResult.OK)
				{
					Deleter.DeleteButton(_currentButton.SourceElement);

					//clear selection of the deleted button
					_currentButton = null;
					XmlDetailStack.Children.Clear();
					SelectedButtonIdText.Text = string.Empty;
					RenderRibbon();
				}
			}
			else if (_currentGroup != null)
			{
				//Delete group:
				if (Deleter.GroupCanBeDeleted(_currentGroup.SourceElement))
				{
					MessageBoxResult result =
						MessageBox.Show("Are you sure you want to delete the selected group?",
							"Delete Ribbon Group", MessageBoxButton.OKCancel);

					if (result == MessageBoxResult.OK)
					{
						Deleter.DeleteGroup(_currentGroup.SourceElement);

						//clear selection of the deleted group
						_currentGroup = null;
						XmlDetailStack.Children.Clear();
						SelectedButtonIdText.Text = string.Empty;
						RenderRibbon();
					}
				}
			}


		}

		/// <summary>
		/// User clicks the Hide Button button
		/// </summary>
		private void HideButton_Click(object sender, RoutedEventArgs e)
		{
			if (_currentButton != null)
			{
				string id = _currentButton.SourceElement.AttributeValue("Id");

				if (Hider.IsControlHidden(id))
					Hider.ShowControl(id);
				else
					Hider.HideControl(id);
				DeselectCurrentButtonOrGroup();
				SetButtonEnabled(HideButton, false);
				RenderRibbon();
			}
		}

		private bool WellKnownTabExists(WellKnownTab wkTab)
		{
			foreach (var item in TabStack.Children)
			{
				if (item is Tab)
				{
					if (((Tab)item).WellKnownTab == wkTab) return true;
				}
			}
			return false;
		}

		/// <summary>
		/// User clicks the Hide Tab button
		/// </summary>
		private void HideTab_Click(object sender, RoutedEventArgs e)
		{
			if (_currentTab != null)
			{
				string id = _currentTab.AttributeValue("Id");

				if (Hider.IsControlHidden(id))
				{
					Hider.ShowControl(id);
				}
				else
				{
					// Warn about hiding the last tab
					if ((TabStack.Children.Count == 1) || // last tab
						(TabStack.Children.Count == 2 && Tab.GetWellKnownTab(id) != WellKnownTab.Customize && WellKnownTabExists(WellKnownTab.Customize))) // last tab, except 'Customize'
					{
						MessageBoxResult result = MessageBox.Show("Hiding all tabs might lead to client script errors in CRM application. \r\nContinue?", "Warning", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
						if (result == MessageBoxResult.No) return;
					}
					Hider.HideControl(id);
				}
				_currentTab = null;

				RenderTabs();
				RenderRibbon();
			}
		}

		/// <summary>
		/// Generic method for showing an IDIalog as a popup in the center of the window
		/// </summary>
		/// <param name="dialog">The dialog to show</param>
		/// <param name="callback">this is fired when the dialog is closed</param>
		public void ShowPopupDialog( IDialog dialog, Action<Object> callback)
		{
			ToolBarStack.IsEnabled = false;
			XmlDetailStack.IsEnabled = false;

			FrameworkElement dlg = (FrameworkElement)dialog;
			dlg.VerticalAlignment = System.Windows.VerticalAlignment.Center;
			dlg.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
			DialogBorder.Child = dlg;
			DialogBorder.Visibility = System.Windows.Visibility.Visible;

			dlg.Focus();

			dialog.DialogClosed += (result) =>
			{
				DialogBorder.Child = null;
				DialogBorder.Visibility = System.Windows.Visibility.Collapsed;

				ToolBarStack.IsEnabled = true;
				XmlDetailStack.IsEnabled = true;

				callback(result);
			};
		}

		/// <summary>
		/// Shows an in-progress message to the user (disables everything while an operation is in progress)
		/// </summary>
		private void ShowInProgressMessage(string text)
		{
			InProgressBorder.Visibility = Visibility.Visible;
			InProgressText.Text = text;
			this.IsEnabled = false;
		}

		public void SetInProgressSubText(string text)
		{
			InProgressSubText.Text = "(" + text + ")";
			InProgressSubText.Visibility = string.IsNullOrEmpty(text) ? Visibility.Collapsed : Visibility.Visible;
		}

		/// <summary>
		/// Hide the in progress message
		/// </summary>
		private void HideInPorgressMessage()
		{
			InProgressBorder.Visibility = Visibility.Collapsed;
			InProgressSubText.Visibility = Visibility.Collapsed;
			this.IsEnabled = true;
		}

		/// <summary>
		/// User clicked the "Connection info" button.
		/// </summary>
		private void ConnectionButton_Click(object sender, RoutedEventArgs e)
		{
			//show the conenction info dialog.
			//no need to do anything when dialog returns as connection info will be stored in Connection.CurrentConnection object.
			ConnectDialog dialog = new ConnectDialog();
			ShowPopupDialog(dialog, (result) =>
			{
				if (result != null && (bool)result == true && Connection.CurrentConnection.IsConnected) ViewWebResourcesButton.IsEnabled = true;
			});
		}

		private void UnHideDialogButton_Click(object sender, RoutedEventArgs e)
		{
			if (_solution != null && _solution.RibbonDiffXml != null)
			{
				UnHideDialog dialog = new UnHideDialog();
				if (!dialog.CurrentSolutionHasHiddenItems())
				{
					MessageBox.Show("There are no hidden controls");
					return;
				}

				ShowPopupDialog(dialog, (result) =>
				{
					if (result != null)
					{
						RenderTabs();
						RenderRibbon();
					}
				});
			}
		}

		private void NewGroup_Click(object sender, RoutedEventArgs e)
		{
			if (_currentTab != null)
			{
				XElement groupsElement = _currentTab.Element("Groups");
				NewGroupDialog dialog = new NewGroupDialog(groupsElement);

				ShowPopupDialog(dialog, (result) =>
				{
					if (result != null)
					{
						RenderRibbon();
					}
				});
			}
		}

		/// <summary>
		/// Sets the IsEnabled property of a button and also
		/// sets any images on the button to semi-transparent if its disabled.
		/// </summary>
		public void SetButtonEnabled(Button button, bool enabled, bool force = false)
		{
			if (force == true || button.IsEnabled != enabled)
			{
				button.IsEnabled = enabled;
				Panel p = button.Content as Panel;
				if (p != null)
				{
					foreach (var c in p.Children)
					{
						if (c is Image) (c as Image).Opacity = enabled ? 1.0 : 0.4;
						if (c is TextBlock) (c as TextBlock).Foreground = enabled ? Brushes.Black : Brushes.Gray ;
					}
				}
			}
		}

		private void ViewWebResourcesButton_Click(object sender, RoutedEventArgs e)
		{
			if (Connection.CurrentConnection.IsConnected)
			{
				WebResourceDialog webResourceDialog = new WebResourceDialog(ResourceType.All);
				ShowPopupDialog(webResourceDialog, (result) => { /* nothing */ });
			}
			else
			{
				MessageBox.Show("Please connect to a CRM instance to view Web Resources");
				return;
			}
		}
		/*
		private void NewMenuButton_Click(object sender, RoutedEventArgs e)
		{
			(sender as Button).ContextMenu.IsEnabled = true;
			(sender as Button).ContextMenu.PlacementTarget = (sender as Button);
			(sender as Button).ContextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom;
			(sender as Button).ContextMenu.IsOpen = true;
		}

		private void NewMenuButton_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
		{
			if (e.NewValue == null) return;
			bool value = (bool)e.NewValue;
			NewMenuButton_TextBlock.Foreground = value ? Brushes.Black : Brushes.Gray;
			NewMenuButton_ArrowDown.Fill = value ? Brushes.Black : Brushes.Gray;
		}
		*/
	}
}